home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / PEAR / Registry.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  70.1 KB  |  2,132 lines

  1. <?php
  2. /**
  3.  * PEAR_Registry
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Stig Bakken <ssb@php.net>
  16.  * @author     Tomas V. V. Cox <cox@idecnet.com>
  17.  * @author     Greg Beaver <cellog@php.net>
  18.  * @copyright  1997-2005 The PHP Group
  19.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  20.  * @version    CVS: $Id: Registry.php,v 1.143 2005/10/31 05:06:43 cellog Exp $
  21.  * @link       http://pear.php.net/package/PEAR
  22.  * @since      File available since Release 0.1
  23.  */
  24.  
  25. /**
  26.  * for PEAR_Error
  27.  */
  28. require_once 'PEAR.php';
  29. require_once 'PEAR/DependencyDB.php';
  30.  
  31. define('PEAR_REGISTRY_ERROR_LOCK',   -2);
  32. define('PEAR_REGISTRY_ERROR_FORMAT', -3);
  33. define('PEAR_REGISTRY_ERROR_FILE',   -4);
  34. define('PEAR_REGISTRY_ERROR_CONFLICT', -5);
  35. define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
  36.  
  37. /**
  38.  * Administration class used to maintain the installed package database.
  39.  * @category   pear
  40.  * @package    PEAR
  41.  * @author     Stig Bakken <ssb@php.net>
  42.  * @author     Tomas V. V. Cox <cox@idecnet.com>
  43.  * @author     Greg Beaver <cellog@php.net>
  44.  * @copyright  1997-2005 The PHP Group
  45.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  46.  * @version    Release: 1.4.5
  47.  * @link       http://pear.php.net/package/PEAR
  48.  * @since      Class available since Release 1.4.0a1
  49.  */
  50. class PEAR_Registry extends PEAR
  51. {
  52.     // {{{ properties
  53.  
  54.     /**
  55.      * File containing all channel information.
  56.      * @var string
  57.      */
  58.     var $channels = '';
  59.  
  60.     /** Directory where registry files are stored.
  61.      * @var string
  62.      */
  63.     var $statedir = '';
  64.  
  65.     /** File where the file map is stored
  66.      * @var string
  67.      */
  68.     var $filemap = '';
  69.  
  70.     /** Directory where registry files for channels are stored.
  71.      * @var string
  72.      */
  73.     var $channelsdir = '';
  74.  
  75.     /** Name of file used for locking the registry
  76.      * @var string
  77.      */
  78.     var $lockfile = '';
  79.  
  80.     /** File descriptor used during locking
  81.      * @var resource
  82.      */
  83.     var $lock_fp = null;
  84.  
  85.     /** Mode used during locking
  86.      * @var int
  87.      */
  88.     var $lock_mode = 0; // XXX UNUSED
  89.  
  90.     /** Cache of package information.  Structure:
  91.      * array(
  92.      *   'package' => array('id' => ... ),
  93.      *   ... )
  94.      * @var array
  95.      */
  96.     var $pkginfo_cache = array();
  97.  
  98.     /** Cache of file map.  Structure:
  99.      * array( '/path/to/file' => 'package', ... )
  100.      * @var array
  101.      */
  102.     var $filemap_cache = array();
  103.  
  104.     /**
  105.      * @var false|PEAR_ChannelFile
  106.      */
  107.     var $_pearChannel;
  108.  
  109.     /**
  110.      * @var false|PEAR_ChannelFile
  111.      */
  112.     var $_peclChannel;
  113.  
  114.     /**
  115.      * @var PEAR_DependencyDB
  116.      */
  117.     var $_dependencyDB;
  118.  
  119.     /**
  120.      * @var PEAR_Config
  121.      */
  122.     var $_config;
  123.     // }}}
  124.  
  125.     // {{{ constructor
  126.  
  127.     /**
  128.      * PEAR_Registry constructor.
  129.      *
  130.      * @param string (optional) PEAR install directory (for .php files)
  131.      * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
  132.      *        default values are not desired.  Only used the very first time a PEAR
  133.      *        repository is initialized
  134.      * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
  135.      *        default values are not desired.  Only used the very first time a PEAR
  136.      *        repository is initialized
  137.      *
  138.      * @access public
  139.      */
  140.     function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
  141.                            $pecl_channel = false)
  142.     {
  143.         parent::PEAR();
  144.         $ds = DIRECTORY_SEPARATOR;
  145.         $this->install_dir = $pear_install_dir;
  146.         $this->channelsdir = $pear_install_dir.$ds.'.channels';
  147.         $this->statedir = $pear_install_dir.$ds.'.registry';
  148.         $this->filemap  = $pear_install_dir.$ds.'.filemap';
  149.         $this->lockfile = $pear_install_dir.$ds.'.lock';
  150.         $this->_pearChannel = $pear_channel;
  151.         $this->_peclChannel = $pecl_channel;
  152.         $this->_config = false;
  153.     }
  154.  
  155.     function hasWriteAccess()
  156.     {
  157.         if (!@file_exists($this->install_dir)) {
  158.             $dir = $this->install_dir;
  159.             while ($dir && $dir != '.') {
  160.                 $dir = dirname($dir); // cd ..
  161.                 if ($dir != '.' && @file_exists($dir)) {
  162.                     if (@is_writeable($dir)) {
  163.                         return true;
  164.                     } else {
  165.                         return false;
  166.                     }
  167.                 }
  168.             }
  169.             return false;
  170.         }
  171.         return @is_writeable($this->install_dir);
  172.     }
  173.  
  174.     function setConfig(&$config)
  175.     {
  176.         $this->_config = &$config;
  177.     }
  178.  
  179.     function _initializeChannelDirs()
  180.     {
  181.         static $running = false;
  182.         if (!$running) {
  183.             $running = true;
  184.             $ds = DIRECTORY_SEPARATOR;
  185.             if (!is_dir($this->channelsdir) ||
  186.                   !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
  187.                   !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
  188.                   !file_exists($this->channelsdir . $ds . '__uri.reg')) {
  189.                 if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  190.                     $pear_channel = $this->_pearChannel;
  191.                     if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
  192.                         if (!class_exists('PEAR_ChannelFile')) {
  193.                             require_once 'PEAR/ChannelFile.php';
  194.                         }
  195.                         $pear_channel = new PEAR_ChannelFile;
  196.                         $pear_channel->setName('pear.php.net');
  197.                         $pear_channel->setAlias('pear');
  198.                         $pear_channel->setServer('pear.php.net');
  199.                         $pear_channel->setSummary('PHP Extension and Application Repository');
  200.                         $pear_channel->setDefaultPEARProtocols();
  201.                         $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
  202.                         $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
  203.                     } else {
  204.                         $pear_channel->setName('pear.php.net');
  205.                         $pear_channel->setAlias('pear');
  206.                     }
  207.                     $pear_channel->validate();
  208.                     $this->_addChannel($pear_channel);
  209.                 }
  210.                 if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
  211.                     $pecl_channel = $this->_peclChannel;
  212.                     if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
  213.                         if (!class_exists('PEAR_ChannelFile')) {
  214.                             require_once 'PEAR/ChannelFile.php';
  215.                         }
  216.                         $pecl_channel = new PEAR_ChannelFile;
  217.                         $pecl_channel->setName('pecl.php.net');
  218.                         $pecl_channel->setAlias('pecl');
  219.                         $pecl_channel->setServer('pecl.php.net');
  220.                         $pecl_channel->setSummary('PHP Extension Community Library');
  221.                         $pecl_channel->setDefaultPEARProtocols();
  222.                         $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
  223.                         $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
  224.                         $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
  225.                     } else {
  226.                         $pecl_channel->setName('pecl.php.net');
  227.                         $pecl_channel->setAlias('pecl');
  228.                     }
  229.                     $pecl_channel->validate();
  230.                     $this->_addChannel($pecl_channel);
  231.                 }
  232.                 if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
  233.                     if (!class_exists('PEAR_ChannelFile')) {
  234.                         require_once 'PEAR/ChannelFile.php';
  235.                     }
  236.                     $private = new PEAR_ChannelFile;
  237.                     $private->setName('__uri');
  238.                     $private->addFunction('xmlrpc', '1.0', '****');
  239.                     $private->setSummary('Pseudo-channel for static packages');
  240.                     $this->_addChannel($private);
  241.                 }
  242.                 $this->_rebuildFileMap();
  243.             }
  244.             $running = false;
  245.         }
  246.     }
  247.  
  248.     function _initializeDirs()
  249.     {
  250.         $ds = DIRECTORY_SEPARATOR;
  251.         // XXX Compatibility code should be removed in the future
  252.         // rename all registry files if any to lowercase
  253.         if (!OS_WINDOWS && $handle = @opendir($this->statedir)) {
  254.             $dest = $this->statedir . $ds;
  255.             while (false !== ($file = readdir($handle))) {
  256.                 if (preg_match('/^.*[A-Z].*\.reg$/', $file)) {
  257.                     rename($dest . $file, $dest . strtolower($file));
  258.                 }
  259.             }
  260.             closedir($handle);
  261.         }
  262.         $this->_initializeChannelDirs();
  263.         if (!file_exists($this->filemap)) {
  264.             $this->_rebuildFileMap();
  265.         }
  266.         $this->_initializeDepDB();
  267.     }
  268.  
  269.     function _initializeDepDB()
  270.     {
  271.         if (!isset($this->_dependencyDB)) {
  272.             static $initializing = false;
  273.             if (!$initializing) {
  274.                 $initializing = true;
  275.                 if (!$this->_config) { // never used?
  276.                     if (OS_WINDOWS) {
  277.                         $file = 'pear.ini';
  278.                     } else {
  279.                         $file = '.pearrc';
  280.                     }
  281.                     $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
  282.                         $file);
  283.                     $this->_config->setRegistry($this);
  284.                     $this->_config->set('php_dir', $this->install_dir);
  285.                 }
  286.                 $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
  287.                 if (PEAR::isError($this->_dependencyDB)) {
  288.                     // attempt to recover by removing the dep db
  289.                     @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
  290.                         DIRECTORY_SEPARATOR . '.depdb');
  291.                     $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
  292.                     if (PEAR::isError($this->_dependencyDB)) {
  293.                         echo $this->_dependencyDB->getMessage();
  294.                         die('Unrecoverable error');
  295.                     }
  296.                 }
  297.                 $initializing = false;
  298.             }
  299.         }
  300.     }
  301.     // }}}
  302.     // {{{ destructor
  303.  
  304.     /**
  305.      * PEAR_Registry destructor.  Makes sure no locks are forgotten.
  306.      *
  307.      * @access private
  308.      */
  309.     function _PEAR_Registry()
  310.     {
  311.         parent::_PEAR();
  312.         if (is_resource($this->lock_fp)) {
  313.             $this->_unlock();
  314.         }
  315.     }
  316.  
  317.     // }}}
  318.  
  319.     // {{{ _assertStateDir()
  320.  
  321.     /**
  322.      * Make sure the directory where we keep registry files exists.
  323.      *
  324.      * @return bool TRUE if directory exists, FALSE if it could not be
  325.      * created
  326.      *
  327.      * @access private
  328.      */
  329.     function _assertStateDir($channel = false)
  330.     {
  331.         if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  332.             return $this->_assertChannelStateDir($channel);
  333.         }
  334.         static $init = false;
  335.         if (!@is_dir($this->statedir)) {
  336.             if (!$this->hasWriteAccess()) {
  337.                 return false;
  338.             }
  339.             require_once 'System.php';
  340.             if (!System::mkdir(array('-p', $this->statedir))) {
  341.                 return $this->raiseError("could not create directory '{$this->statedir}'");
  342.             }
  343.             $init = true;
  344.         }
  345.         $ds = DIRECTORY_SEPARATOR;
  346.         if (!@is_dir($this->channelsdir) ||
  347.               !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
  348.               !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
  349.               !file_exists($this->channelsdir . $ds . '__uri.reg')) {
  350.             $init = true;
  351.         }
  352.         if ($init) {
  353.             static $running = false;
  354.             if (!$running) {
  355.                 $running = true;
  356.                 $this->_initializeDirs();
  357.                 $running = false;
  358.                 $init = false;
  359.             }
  360.         } else {
  361.             $this->_initializeDepDB();
  362.         }
  363.         return true;
  364.     }
  365.  
  366.     // }}}
  367.     // {{{ _assertChannelStateDir()
  368.  
  369.     /**
  370.      * Make sure the directory where we keep registry files exists for a non-standard channel.
  371.      *
  372.      * @param string channel name
  373.      * @return bool TRUE if directory exists, FALSE if it could not be
  374.      * created
  375.      *
  376.      * @access private
  377.      */
  378.     function _assertChannelStateDir($channel)
  379.     {
  380.         $ds = DIRECTORY_SEPARATOR;
  381.         if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  382.             if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  383.                 $this->_initializeChannelDirs();
  384.             }
  385.             return $this->_assertStateDir($channel);
  386.         }
  387.         $channelDir = $this->_channelDirectoryName($channel);
  388.         if (!is_dir($this->channelsdir) ||
  389.               !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  390.             $this->_initializeChannelDirs();
  391.         }
  392.         if (!@is_dir($channelDir)) {
  393.             if (!$this->hasWriteAccess()) {
  394.                 return false;
  395.             }
  396.             require_once 'System.php';
  397.             if (!System::mkdir(array('-p', $channelDir))) {
  398.                 return $this->raiseError("could not create directory '" . $channelDir .
  399.                     "'");
  400.             }
  401.         }
  402.         return true;
  403.     }
  404.  
  405.     // }}}
  406.     // {{{ _assertChannelDir()
  407.  
  408.     /**
  409.      * Make sure the directory where we keep registry files for channels exists
  410.      *
  411.      * @return bool TRUE if directory exists, FALSE if it could not be
  412.      * created
  413.      *
  414.      * @access private
  415.      */
  416.     function _assertChannelDir()
  417.     {
  418.         if (!@is_dir($this->channelsdir)) {
  419.             if (!$this->hasWriteAccess()) {
  420.                 return false;
  421.             }
  422.             require_once 'System.php';
  423.             if (!System::mkdir(array('-p', $this->channelsdir))) {
  424.                 return $this->raiseError("could not create directory '{$this->channelsdir}'");
  425.             }
  426.         }
  427.         if (!@is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
  428.             if (!$this->hasWriteAccess()) {
  429.                 return false;
  430.             }
  431.             require_once 'System.php';
  432.             if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
  433.                 return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
  434.             }
  435.         }
  436.         return true;
  437.     }
  438.  
  439.     // }}}
  440.     // {{{ _packageFileName()
  441.  
  442.     /**
  443.      * Get the name of the file where data for a given package is stored.
  444.      *
  445.      * @param string channel name, or false if this is a PEAR package
  446.      * @param string package name
  447.      *
  448.      * @return string registry file name
  449.      *
  450.      * @access public
  451.      */
  452.     function _packageFileName($package, $channel = false)
  453.     {
  454.         if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  455.             return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
  456.                 strtolower($package) . '.reg';
  457.         }
  458.         return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
  459.     }
  460.  
  461.     // }}}
  462.     // {{{ _channelFileName()
  463.  
  464.     /**
  465.      * Get the name of the file where data for a given channel is stored.
  466.      * @param string channel name
  467.      * @return string registry file name
  468.      */
  469.     function _channelFileName($channel, $noaliases = false)
  470.     {
  471.         if (!$noaliases) {
  472.             if (@file_exists($this->_getChannelAliasFileName($channel))) {
  473.                 $channel = implode('', file($this->_getChannelAliasFileName($channel)));
  474.             }
  475.         }
  476.         return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
  477.             strtolower($channel)) . '.reg';
  478.     }
  479.  
  480.     // }}}
  481.     // {{{ getChannelAliasFileName()
  482.  
  483.     /**
  484.      * @param string
  485.      * @return string
  486.      */
  487.     function _getChannelAliasFileName($alias)
  488.     {
  489.         return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
  490.               DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
  491.     }
  492.  
  493.     // }}}
  494.     // {{{ _getChannelFromAlias()
  495.  
  496.     /**
  497.      * Get the name of a channel from its alias
  498.      */
  499.     function _getChannelFromAlias($channel)
  500.     {
  501.         if (!$this->_channelExists($channel)) {
  502.             if ($channel == 'pear.php.net') {
  503.                 return 'pear.php.net';
  504.             }
  505.             if ($channel == 'pecl.php.net') {
  506.                 return 'pecl.php.net';
  507.             }
  508.             if ($channel == '__uri') {
  509.                 return '__uri';
  510.             }
  511.             return false;
  512.         }
  513.         $channel = strtolower($channel);
  514.         if (file_exists($this->_getChannelAliasFileName($channel))) {
  515.             // translate an alias to an actual channel
  516.             return implode('', file($this->_getChannelAliasFileName($channel)));
  517.         } else {
  518.             return $channel;
  519.         }
  520.     }    
  521.     // }}}
  522.     // {{{ _getChannelFromAlias()
  523.  
  524.     /**
  525.      * Get the alias of a channel from its alias or its name
  526.      */
  527.     function _getAlias($channel)
  528.     {
  529.         if (!$this->_channelExists($channel)) {
  530.             if ($channel == 'pear.php.net') {
  531.                 return 'pear';
  532.             }
  533.             if ($channel == 'pecl.php.net') {
  534.                 return 'pecl';
  535.             }
  536.             return false;
  537.         }
  538.         $channel = $this->_getChannel($channel);
  539.         return $channel->getAlias();
  540.     }    
  541.     // }}}
  542.     // {{{ _channelDirectoryName()
  543.  
  544.     /**
  545.      * Get the name of the file where data for a given package is stored.
  546.      *
  547.      * @param string channel name, or false if this is a PEAR package
  548.      * @param string package name
  549.      *
  550.      * @return string registry file name
  551.      *
  552.      * @access public
  553.      */
  554.     function _channelDirectoryName($channel)
  555.     {
  556.         if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  557.             return $this->statedir;
  558.         } else {
  559.             $ch = $this->_getChannelFromAlias($channel);
  560.             if (!$ch) {
  561.                 $ch = $channel;
  562.             }
  563.             return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
  564.                 str_replace('/', '_', $ch));
  565.         }
  566.     }
  567.  
  568.     // }}}
  569.     // {{{ _openPackageFile()
  570.  
  571.     function _openPackageFile($package, $mode, $channel = false)
  572.     {
  573.         if (!$this->_assertStateDir($channel)) {
  574.             return null;
  575.         }
  576.         if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
  577.             return null;
  578.         }
  579.         $file = $this->_packageFileName($package, $channel);
  580.         $fp = @fopen($file, $mode);
  581.         if (!$fp) {
  582.             return null;
  583.         }
  584.         return $fp;
  585.     }
  586.  
  587.     // }}}
  588.     // {{{ _closePackageFile()
  589.  
  590.     function _closePackageFile($fp)
  591.     {
  592.         fclose($fp);
  593.     }
  594.  
  595.     // }}}
  596.     // {{{ _openPackageFile()
  597.  
  598.     function _openChannelFile($channel, $mode)
  599.     {
  600.         if (!$this->_assertChannelDir()) {
  601.             return null;
  602.         }
  603.         if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
  604.             return null;
  605.         }
  606.         $file = $this->_channelFileName($channel);
  607.         $fp = @fopen($file, $mode);
  608.         if (!$fp) {
  609.             return null;
  610.         }
  611.         return $fp;
  612.     }
  613.  
  614.     // }}}
  615.     // {{{ _closePackageFile()
  616.  
  617.     function _closeChannelFile($fp)
  618.     {
  619.         fclose($fp);
  620.     }
  621.  
  622.     // }}}
  623.     // {{{ _rebuildFileMap()
  624.  
  625.     function _rebuildFileMap()
  626.     {
  627.         if (!class_exists('PEAR_Installer_Role')) {
  628.             require_once 'PEAR/Installer/Role.php';
  629.         }
  630.         $channels = $this->_listAllPackages();
  631.         $files = array();
  632.         foreach ($channels as $channel => $packages) {
  633.             foreach ($packages as $package) {
  634.                 $version = $this->_packageInfo($package, 'version', $channel);
  635.                 $filelist = $this->_packageInfo($package, 'filelist', $channel);
  636.                 if (!is_array($filelist)) {
  637.                     continue;
  638.                 }
  639.                 foreach ($filelist as $name => $attrs) {
  640.                     if (isset($attrs['attribs'])) {
  641.                         $attrs = $attrs['attribs'];
  642.                     }
  643.                     // it is possible for conflicting packages in different channels to
  644.                     // conflict with data files/doc files
  645.                     if ($name == 'dirtree') {
  646.                         continue;
  647.                     }
  648.                     if (isset($attrs['role']) && !in_array($attrs['role'],
  649.                           PEAR_Installer_Role::getInstallableRoles())) {
  650.                         // these are not installed
  651.                         continue;
  652.                     }
  653.                     if (isset($attrs['role']) && !in_array($attrs['role'],
  654.                           PEAR_Installer_Role::getBaseinstallRoles())) {
  655.                         $attrs['baseinstalldir'] = $package;
  656.                     }
  657.                     if (isset($attrs['baseinstalldir'])) {
  658.                         $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
  659.                     } else {
  660.                         $file = $name;
  661.                     }
  662.                     $file = preg_replace(',^/+,', '', $file);
  663.                     if ($channel != 'pear.php.net') {
  664.                         $files[$attrs['role']][$file] = array(strtolower($channel),
  665.                             strtolower($package));
  666.                     } else {
  667.                         $files[$attrs['role']][$file] = strtolower($package);
  668.                     }
  669.                 }
  670.             }
  671.         }
  672.         $this->_assertStateDir();
  673.         if (!$this->hasWriteAccess()) {
  674.             return false;
  675.         }
  676.         $fp = @fopen($this->filemap, 'wb');
  677.         if (!$fp) {
  678.             return false;
  679.         }
  680.         $this->filemap_cache = $files;
  681.         fwrite($fp, serialize($files));
  682.         fclose($fp);
  683.         return true;
  684.     }
  685.  
  686.     // }}}
  687.     // {{{ _readFileMap()
  688.  
  689.     function _readFileMap()
  690.     {
  691.         $fp = @fopen($this->filemap, 'r');
  692.         if (!$fp) {
  693.             return $this->raiseError('PEAR_Registry: could not open filemap', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
  694.         }
  695.         clearstatcache();
  696.         $rt = get_magic_quotes_runtime();
  697.         set_magic_quotes_runtime(0);
  698.         $fsize = filesize($this->filemap);
  699.         if (function_exists('file_get_contents')) {
  700.             fclose($fp);
  701.             $data = file_get_contents($this->filemap);
  702.         } else {
  703.             $data = fread($fp, $fsize);
  704.             fclose($fp);
  705.         }
  706.         set_magic_quotes_runtime($rt);
  707.         $tmp = unserialize($data);
  708.         if (!$tmp && $fsize > 7) {
  709.             return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
  710.         }
  711.         $this->filemap_cache = $tmp;
  712.         return true;
  713.     }
  714.  
  715.     // }}}
  716.     // {{{ _lock()
  717.  
  718.     /**
  719.      * Lock the registry.
  720.      *
  721.      * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
  722.      *                See flock manual for more information.
  723.      *
  724.      * @return bool TRUE on success, FALSE if locking failed, or a
  725.      *              PEAR error if some other error occurs (such as the
  726.      *              lock file not being writable).
  727.      *
  728.      * @access private
  729.      */
  730.     function _lock($mode = LOCK_EX)
  731.     {
  732.         if (!eregi('Windows 9', php_uname())) {
  733.             if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
  734.                 // XXX does not check type of lock (LOCK_SH/LOCK_EX)
  735.                 return true;
  736.             }
  737.             if (!$this->_assertStateDir()) {
  738.                 if ($mode == LOCK_EX) {
  739.                     return $this->raiseError('Registry directory is not writeable by the current user');
  740.                 } else {
  741.                     return true;
  742.                 }
  743.             }
  744.             $open_mode = 'w';
  745.             // XXX People reported problems with LOCK_SH and 'w'
  746.             if ($mode === LOCK_SH || $mode === LOCK_UN) {
  747.                 if (@!is_file($this->lockfile)) {
  748.                     touch($this->lockfile);
  749.                 }
  750.                 $open_mode = 'r';
  751.             }
  752.  
  753.             if (!is_resource($this->lock_fp)) {
  754.                 $this->lock_fp = @fopen($this->lockfile, $open_mode);
  755.             }
  756.  
  757.             if (!is_resource($this->lock_fp)) {
  758.                 return $this->raiseError("could not create lock file" .
  759.                                          (isset($php_errormsg) ? ": " . $php_errormsg : ""));
  760.             }
  761.             if (!(int)flock($this->lock_fp, $mode)) {
  762.                 switch ($mode) {
  763.                     case LOCK_SH: $str = 'shared';    break;
  764.                     case LOCK_EX: $str = 'exclusive'; break;
  765.                     case LOCK_UN: $str = 'unlock';    break;
  766.                     default:      $str = 'unknown';   break;
  767.                 }
  768.                 return $this->raiseError("could not acquire $str lock ($this->lockfile)",
  769.                                          PEAR_REGISTRY_ERROR_LOCK);
  770.             }
  771.         }
  772.         return true;
  773.     }
  774.  
  775.     // }}}
  776.     // {{{ _unlock()
  777.  
  778.     function _unlock()
  779.     {
  780.         $ret = $this->_lock(LOCK_UN);
  781.         if (is_resource($this->lock_fp)) {
  782.             fclose($this->lock_fp);
  783.         }
  784.         $this->lock_fp = null;
  785.         return $ret;
  786.     }
  787.  
  788.     // }}}
  789.     // {{{ _packageExists()
  790.  
  791.     function _packageExists($package, $channel = false)
  792.     {
  793.         return file_exists($this->_packageFileName($package, $channel));
  794.     }
  795.  
  796.     // }}}
  797.     // {{{ _channelExists()
  798.  
  799.     /**
  800.      * Determine whether a channel exists in the registry
  801.      * @param string Channel name
  802.      * @param bool if true, then aliases will be ignored
  803.      * @return boolean
  804.      */
  805.     function _channelExists($channel, $noaliases = false)
  806.     {
  807.         $a = file_exists($this->_channelFileName($channel, $noaliases));
  808.         if (!$a && $channel == 'pear.php.net') {
  809.             return true;
  810.         }
  811.         if (!$a && $channel == 'pecl.php.net') {
  812.             return true;
  813.         }
  814.         return $a;
  815.     }
  816.  
  817.     // }}}
  818.     // {{{ _addChannel()
  819.  
  820.     /**
  821.      * @param PEAR_ChannelFile Channel object
  822.      * @param donotuse
  823.      * @param string Last-Modified HTTP tag from remote request
  824.      * @return boolean|PEAR_Error True on creation, false if it already exists
  825.      */
  826.     function _addChannel($channel, $update = false, $lastmodified = false)
  827.     {
  828.         if (!is_a($channel, 'PEAR_ChannelFile')) {
  829.             return false;
  830.         }
  831.         if (!$channel->validate()) {
  832.             return false;
  833.         }
  834.         if (file_exists($this->_channelFileName($channel->getName()))) {
  835.             if (!$update) {
  836.                 return false;
  837.             }
  838.             $checker = $this->_getChannel($channel->getName());
  839.             if ($channel->getAlias() != $checker->getAlias()) {
  840.                 @unlink($this->_getChannelAliasFileName($checker->getAlias()));
  841.             }
  842.         } else {
  843.             if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net'))) {
  844.                 return false;
  845.             }
  846.         }
  847.         $ret = $this->_assertChannelDir();
  848.         if (PEAR::isError($ret)) {
  849.             return $ret;
  850.         }
  851.         $ret = $this->_assertChannelStateDir($channel->getName());
  852.         if (PEAR::isError($ret)) {
  853.             return $ret;
  854.         }
  855.         if ($channel->getAlias() != $channel->getName()) {
  856.             if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
  857.                   $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
  858.                 $channel->setAlias($channel->getName());
  859.             }
  860.             if (!$this->hasWriteAccess()) {
  861.                 return false;
  862.             }
  863.             $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
  864.             if (!$fp) {
  865.                 return false;
  866.             }
  867.             fwrite($fp, $channel->getName());
  868.             fclose($fp);
  869.         }
  870.         if (!$this->hasWriteAccess()) {
  871.             return false;
  872.         }
  873.         $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
  874.         if (!$fp) {
  875.             return false;
  876.         }
  877.         $info = $channel->toArray();
  878.         if ($lastmodified) {
  879.             $info['_lastmodified'] = $lastmodified;
  880.         } else {
  881.             $info['_lastmodified'] = date('r');
  882.         }
  883.         fwrite($fp, serialize($info));
  884.         fclose($fp);
  885.         return true;
  886.     }
  887.  
  888.     // }}}
  889.     // {{{ _deleteChannel()
  890.  
  891.     /**
  892.      * Deletion fails if there are any packages installed from the channel
  893.      * @param string|PEAR_ChannelFile channel name
  894.      * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
  895.      */
  896.     function _deleteChannel($channel)
  897.     {
  898.         if (!is_string($channel)) {
  899.             if (is_a($channel, 'PEAR_ChannelFile')) {
  900.                 if (!$channel->validate()) {
  901.                     return false;
  902.                 }
  903.                 $channel = $channel->getName();
  904.             } else {
  905.                 return false;
  906.             }
  907.         }
  908.         if ($this->_getChannelFromAlias($channel) == '__uri') {
  909.             return false;
  910.         }
  911.         if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
  912.             return false;
  913.         }
  914.         if (!$this->_channelExists($channel)) {
  915.             return false;
  916.         }
  917.         if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  918.             return false;
  919.         }
  920.         $channel = $this->_getChannelFromAlias($channel);
  921.         if ($channel == 'pear.php.net') {
  922.             return false;
  923.         }
  924.         $test = $this->_listChannelPackages($channel);
  925.         if (count($test)) {
  926.             return false;
  927.         }
  928.         $test = @rmdir($this->_channelDirectoryName($channel));
  929.         if (!$test) {
  930.             return false;
  931.         }
  932.         $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
  933.         if (@file_exists($file)) {
  934.             $test = @unlink($file);
  935.             if (!$test) {
  936.                 return false;
  937.             }
  938.         }
  939.         $file = $this->_channelFileName($channel);
  940.         $ret = @unlink($file);
  941.         return $ret;
  942.     }
  943.  
  944.     // }}}
  945.     // {{{ _isChannelAlias()
  946.  
  947.     /**
  948.      * Determine whether a channel exists in the registry
  949.      * @param string Channel Alias
  950.      * @return boolean
  951.      */
  952.     function _isChannelAlias($alias)
  953.     {
  954.         return file_exists($this->_getChannelAliasFileName($alias));
  955.     }
  956.  
  957.     // }}}
  958.     // {{{ _packageInfo()
  959.  
  960.     /**
  961.      * @param string|null
  962.      * @param string|null
  963.      * @param string|null
  964.      * @return array|null
  965.      * @access private
  966.      */
  967.     function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
  968.     {
  969.         if ($package === null) {
  970.             if ($channel === null) {
  971.                 $channels = $this->_listChannels();
  972.                 $ret = array();
  973.                 foreach ($channels as $channel) {
  974.                     $channel = strtolower($channel);
  975.                     $ret[$channel] = array();
  976.                     $packages = $this->_listPackages($channel);
  977.                     foreach ($packages as $package) {
  978.                         $ret[$channel][] = $this->_packageInfo($package, null, $channel);
  979.                     }
  980.                 }
  981.                 return $ret;
  982.             }
  983.             $ps = $this->_listPackages($channel);
  984.             if (!count($ps)) {
  985.                 return array();
  986.             }
  987.             return array_map(array(&$this, '_packageInfo'),
  988.                              $ps, array_fill(0, count($ps), null),
  989.                              array_fill(0, count($ps), $channel));
  990.         }
  991.         $fp = $this->_openPackageFile($package, 'r', $channel);
  992.         if ($fp === null) {
  993.             return null;
  994.         }
  995.         $rt = get_magic_quotes_runtime();
  996.         set_magic_quotes_runtime(0);
  997.         clearstatcache();
  998.         if (function_exists('file_get_contents')) {
  999.             $this->_closePackageFile($fp);
  1000.             $data = file_get_contents($this->_packageFileName($package, $channel));
  1001.         } else {
  1002.             $data = fread($fp, filesize($this->_packageFileName($package, $channel)));
  1003.             $this->_closePackageFile($fp);
  1004.         }
  1005.         set_magic_quotes_runtime($rt);
  1006.         $data = unserialize($data);
  1007.         if ($key === null) {
  1008.             return $data;
  1009.         }
  1010.         // compatibility for package.xml version 2.0
  1011.         if (isset($data['old'][$key])) {
  1012.             return $data['old'][$key];
  1013.         }
  1014.         if (isset($data[$key])) {
  1015.             return $data[$key];
  1016.         }
  1017.         return null;
  1018.     }
  1019.  
  1020.     // }}}
  1021.     // {{{ _channelInfo()
  1022.  
  1023.     /**
  1024.      * @param string Channel name
  1025.      * @param bool whether to strictly retrieve info of channels, not just aliases
  1026.      * @return array|null
  1027.      */
  1028.     function _channelInfo($channel, $noaliases = false)
  1029.     {
  1030.         if (!$this->_channelExists($channel, $noaliases)) {
  1031.             return null;
  1032.         }
  1033.         $fp = $this->_openChannelFile($channel, 'r');
  1034.         if ($fp === null) {
  1035.             return null;
  1036.         }
  1037.         $rt = get_magic_quotes_runtime();
  1038.         set_magic_quotes_runtime(0);
  1039.         clearstatcache();
  1040.         if (function_exists('file_get_contents')) {
  1041.             $this->_closeChannelFile($fp);
  1042.             $data = file_get_contents($this->_channelFileName($channel));
  1043.         } else {
  1044.             $data = fread($fp, filesize($this->_channelFileName($channel)));
  1045.             $this->_closeChannelFile($fp);
  1046.         }
  1047.         set_magic_quotes_runtime($rt);
  1048.         $data = unserialize($data);
  1049.         return $data;
  1050.     }
  1051.  
  1052.     // }}}
  1053.     // {{{ _listChannels()
  1054.  
  1055.     function _listChannels()
  1056.     {
  1057.         $channellist = array();
  1058.         $dp = @opendir($this->channelsdir);
  1059.         if (!$dp  || !@is_dir($this->channelsdir)) {
  1060.             return array('pear.php.net', 'pecl.php.net', '__uri');
  1061.         }
  1062.         while ($ent = readdir($dp)) {
  1063.             if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
  1064.                 continue;
  1065.             }
  1066.             $channellist[] = substr($ent, 0, -4);
  1067.         }
  1068.         closedir($dp);
  1069.         if (!in_array('pear.php.net', $channellist)) {
  1070.             $channellist[] = 'pear.php.net';
  1071.         }
  1072.         if (!in_array('pecl.php.net', $channellist)) {
  1073.             $channellist[] = 'pecl.php.net';
  1074.         }
  1075.         if (!in_array('__uri', $channellist)) {
  1076.             $channellist[] = '__uri';
  1077.         }
  1078.         return $channellist;
  1079.     }
  1080.  
  1081.     // }}}
  1082.     // {{{ _listPackages()
  1083.  
  1084.     function _listPackages($channel = false)
  1085.     {
  1086.         if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  1087.             return $this->_listChannelPackages($channel);
  1088.         }
  1089.         $pkglist = array();
  1090.         $dp = @opendir($this->statedir);
  1091.         if (!$dp) {
  1092.             return $pkglist;
  1093.         }
  1094.         while ($ent = readdir($dp)) {
  1095.             if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
  1096.                 continue;
  1097.             }
  1098.             $pkglist[] = substr($ent, 0, -4);
  1099.         }
  1100.         closedir($dp);
  1101.         return $pkglist;
  1102.     }
  1103.  
  1104.     // }}}
  1105.     // {{{ _listChannelPackages()
  1106.  
  1107.     function _listChannelPackages($channel)
  1108.     {
  1109.         $pkglist = array();
  1110.         $dp = @opendir($this->_channelDirectoryName($channel));
  1111.         if (!$dp) {
  1112.             return $pkglist;
  1113.         }
  1114.         while ($ent = readdir($dp)) {
  1115.             if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
  1116.                 continue;
  1117.             }
  1118.             $pkglist[] = substr($ent, 0, -4);
  1119.         }
  1120.         closedir($dp);
  1121.         return $pkglist;
  1122.     }
  1123.  
  1124.     // }}}
  1125.     
  1126.     function _listAllPackages()
  1127.     {
  1128.         $ret = array();
  1129.         foreach ($this->_listChannels() as $channel) {
  1130.             $ret[$channel] = $this->_listPackages($channel);
  1131.         }
  1132.         return $ret;
  1133.     }
  1134.  
  1135.     /**
  1136.      * Add an installed package to the registry
  1137.      * @param string package name
  1138.      * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
  1139.      * @return bool success of saving
  1140.      * @access private
  1141.      */
  1142.     function _addPackage($package, $info)
  1143.     {
  1144.         if ($this->_packageExists($package)) {
  1145.             return false;
  1146.         }
  1147.         $fp = $this->_openPackageFile($package, 'wb');
  1148.         if ($fp === null) {
  1149.             return false;
  1150.         }
  1151.         $info['_lastmodified'] = time();
  1152.         fwrite($fp, serialize($info));
  1153.         $this->_closePackageFile($fp);
  1154.         if (isset($info['filelist'])) {
  1155.             $this->_rebuildFileMap();
  1156.         }
  1157.         return true;
  1158.     }
  1159.  
  1160.     /**
  1161.      * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  1162.      * @return bool
  1163.      * @access private
  1164.      */
  1165.     function _addPackage2($info)
  1166.     {
  1167.         if (!$info->validate()) {
  1168.             if (class_exists('PEAR_Common')) {
  1169.                 $ui = PEAR_Frontend::singleton();
  1170.                 if ($ui) {
  1171.                     foreach ($info->getValidationWarnings() as $err) {
  1172.                         $ui->log(2, $err['message']);
  1173.                     }
  1174.                 }
  1175.             }
  1176.             return false;
  1177.         }
  1178.         $channel = $info->getChannel();
  1179.         $package = $info->getPackage();
  1180.         $save = $info;
  1181.         if ($this->_packageExists($package, $channel)) {
  1182.             return false;
  1183.         }
  1184.         if (!$this->_channelExists($channel, true)) {
  1185.             return false;
  1186.         }
  1187.         $info = $info->toArray(true);
  1188.         if (!$info) {
  1189.             return false;
  1190.         }
  1191.         $fp = $this->_openPackageFile($package, 'wb', $channel);
  1192.         if ($fp === null) {
  1193.             return false;
  1194.         }
  1195.         $info['_lastmodified'] = time();
  1196.         fwrite($fp, serialize($info));
  1197.         $this->_closePackageFile($fp);
  1198.         $this->_rebuildFileMap();
  1199.         return true;
  1200.     }
  1201.  
  1202.     /**
  1203.      * @param string Package name
  1204.      * @param array parsed package.xml 1.0
  1205.      * @param bool this parameter is only here for BC.  Don't use it.
  1206.      * @access private
  1207.      */
  1208.     function _updatePackage($package, $info, $merge = true)
  1209.     {
  1210.         $oldinfo = $this->_packageInfo($package);
  1211.         if (empty($oldinfo)) {
  1212.             return false;
  1213.         }
  1214.         $fp = $this->_openPackageFile($package, 'w');
  1215.         if ($fp === null) {
  1216.             return false;
  1217.         }
  1218.         if (is_object($info)) {
  1219.             $info = $info->toArray();
  1220.         }
  1221.         $info['_lastmodified'] = time();
  1222.         $newinfo = $info;
  1223.         if ($merge) {
  1224.             $info = array_merge($oldinfo, $info);
  1225.         } else {
  1226.             $diff = $info;
  1227.         }
  1228.         fwrite($fp, serialize($info));
  1229.         $this->_closePackageFile($fp);
  1230.         if (isset($newinfo['filelist'])) {
  1231.             $this->_rebuildFileMap();
  1232.         }
  1233.         return true;
  1234.     }
  1235.  
  1236.     /**
  1237.      * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  1238.      * @return bool
  1239.      * @access private
  1240.      */
  1241.     function _updatePackage2($info)
  1242.     {
  1243.         if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
  1244.             return false;
  1245.         }
  1246.         $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
  1247.         if ($fp === null) {
  1248.             return false;
  1249.         }
  1250.         $save = $info;
  1251.         $info = $save->getArray(true);
  1252.         $info['_lastmodified'] = time();
  1253.         fwrite($fp, serialize($info));
  1254.         $this->_closePackageFile($fp);
  1255.         $this->_rebuildFileMap();
  1256.         return true;
  1257.     }
  1258.  
  1259.     /**
  1260.      * @param string Package name
  1261.      * @param string Channel name
  1262.      * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
  1263.      * @access private
  1264.      */
  1265.     function &_getPackage($package, $channel = 'pear.php.net')
  1266.     {
  1267.         $info = $this->_packageInfo($package, null, $channel);
  1268.         if ($info === null) {
  1269.             return $info;
  1270.         }
  1271.         $a = $this->_config;
  1272.         if (!$a) {
  1273.             $this->_config = &new PEAR_Config;
  1274.             $this->_config->set('php_dir', $this->statedir);
  1275.         }
  1276.         if (!class_exists('PEAR_PackageFile')) {
  1277.             require_once 'PEAR/PackageFile.php';
  1278.         }
  1279.         $pkg = &new PEAR_PackageFile($this->_config);
  1280.         $pf = &$pkg->fromArray($info);
  1281.         return $pf;
  1282.     }
  1283.  
  1284.     /**
  1285.      * @param string channel name
  1286.      * @param bool whether to strictly retrieve channel names
  1287.      * @return PEAR_ChannelFile|false
  1288.      * @access private
  1289.      */
  1290.     function &_getChannel($channel, $noaliases = false)
  1291.     {
  1292.         $ch = false;
  1293.         if ($this->_channelExists($channel, $noaliases)) {
  1294.             if (!class_exists('PEAR_ChannelFile')) {
  1295.                 require_once 'PEAR/ChannelFile.php';
  1296.             }
  1297.             $ch = &PEAR_ChannelFile::fromArray($this->_channelInfo($channel, $noaliases));
  1298.         }
  1299.         if ($ch) {
  1300.             return $ch;
  1301.         }
  1302.         if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
  1303.             // the registry is not properly set up, so use defaults
  1304.             if (!class_exists('PEAR_ChannelFile')) {
  1305.                 require_once 'PEAR/ChannelFile.php';
  1306.             }
  1307.             $pear_channel = new PEAR_ChannelFile;
  1308.             $pear_channel->setName('pear.php.net');
  1309.             $pear_channel->setAlias('pear');
  1310.             $pear_channel->setSummary('PHP Extension and Application Repository');
  1311.             $pear_channel->setDefaultPEARProtocols();
  1312.             $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
  1313.             $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
  1314.             return $pear_channel;
  1315.         }
  1316.         if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
  1317.             // the registry is not properly set up, so use defaults
  1318.             if (!class_exists('PEAR_ChannelFile')) {
  1319.                 require_once 'PEAR/ChannelFile.php';
  1320.             }
  1321.             $pear_channel = new PEAR_ChannelFile;
  1322.             $pear_channel->setName('pecl.php.net');
  1323.             $pear_channel->setAlias('pecl');
  1324.             $pear_channel->setSummary('PHP Extension Community Library');
  1325.             $pear_channel->setDefaultPEARProtocols();
  1326.             $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
  1327.             $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
  1328.             $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
  1329.             return $pear_channel;
  1330.         }
  1331.         if ($this->_getChannelFromAlias($channel) == '__uri') {
  1332.             // the registry is not properly set up, so use defaults
  1333.             if (!class_exists('PEAR_ChannelFile')) {
  1334.                 require_once 'PEAR/ChannelFile.php';
  1335.             }
  1336.             $private = new PEAR_ChannelFile;
  1337.             $private->setName('__uri');
  1338.             $private->addFunction('xmlrpc', '1.0', '****');
  1339.             $private->setSummary('Pseudo-channel for static packages');
  1340.             return $private;
  1341.         }
  1342.         return $ch;
  1343.     }
  1344.  
  1345.     // {{{ packageExists()
  1346.  
  1347.     /**
  1348.      * @param string Package name
  1349.      * @param string Channel name
  1350.      * @return bool
  1351.      */
  1352.     function packageExists($package, $channel = 'pear.php.net')
  1353.     {
  1354.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1355.             return $e;
  1356.         }
  1357.         $ret = $this->_packageExists($package, $channel);
  1358.         $this->_unlock();
  1359.         return $ret;
  1360.     }
  1361.  
  1362.     // }}}
  1363.  
  1364.     // {{{ channelExists()
  1365.  
  1366.     /**
  1367.      * @param string channel name
  1368.      * @param bool if true, then aliases will be ignored
  1369.      * @return bool
  1370.      */
  1371.     function channelExists($channel, $noaliases = false)
  1372.     {
  1373.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1374.             return $e;
  1375.         }
  1376.         $ret = $this->_channelExists($channel, $noaliases);
  1377.         $this->_unlock();
  1378.         return $ret;
  1379.     }
  1380.  
  1381.     // }}}
  1382.  
  1383.     // {{{ isAlias()
  1384.  
  1385.     /**
  1386.      * Determines whether the parameter is an alias of a channel
  1387.      * @param string
  1388.      * @return bool
  1389.      */
  1390.     function isAlias($alias)
  1391.     {
  1392.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1393.             return $e;
  1394.         }
  1395.         $ret = $this->_isChannelAlias($alias);
  1396.         $this->_unlock();
  1397.         return $ret;
  1398.     }
  1399.  
  1400.     // }}}
  1401.     // {{{ packageInfo()
  1402.  
  1403.     /**
  1404.      * @param string|null
  1405.      * @param string|null
  1406.      * @param string
  1407.      * @return array|null
  1408.      */
  1409.     function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
  1410.     {
  1411.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1412.             return $e;
  1413.         }
  1414.         $ret = $this->_packageInfo($package, $key, $channel);
  1415.         $this->_unlock();
  1416.         return $ret;
  1417.     }
  1418.  
  1419.     // }}}
  1420.     // {{{ channelInfo()
  1421.  
  1422.     /**
  1423.      * Retrieve a raw array of channel data.
  1424.      *
  1425.      * Do not use this, instead use {@link getChannel()} for normal
  1426.      * operations.  Array structure is undefined in this method
  1427.      * @param string channel name
  1428.      * @param bool whether to strictly retrieve information only on non-aliases
  1429.      * @return array|null|PEAR_Error
  1430.      */
  1431.     function channelInfo($channel = null, $noaliases = false)
  1432.     {
  1433.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1434.             return $e;
  1435.         }
  1436.         $ret = $this->_channelInfo($channel, $noaliases);
  1437.         $this->_unlock();
  1438.         return $ret;
  1439.     }
  1440.  
  1441.     // }}}
  1442.  
  1443.     /**
  1444.      * @param string
  1445.      */
  1446.     function channelName($channel)
  1447.     {
  1448.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1449.             return $e;
  1450.         }
  1451.         $ret = $this->_getChannelFromAlias($channel);
  1452.         $this->_unlock();
  1453.         return $ret;
  1454.     }
  1455.  
  1456.     /**
  1457.      * @param string
  1458.      */
  1459.     function channelAlias($channel)
  1460.     {
  1461.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1462.             return $e;
  1463.         }
  1464.         $ret = $this->_getAlias($channel);
  1465.         $this->_unlock();
  1466.         return $ret;
  1467.     }
  1468.     // {{{ listPackages()
  1469.  
  1470.     function listPackages($channel = false)
  1471.     {
  1472.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1473.             return $e;
  1474.         }
  1475.         $ret = $this->_listPackages($channel);
  1476.         $this->_unlock();
  1477.         return $ret;
  1478.     }
  1479.  
  1480.     // }}}
  1481.     // {{{ listAllPackages()
  1482.  
  1483.     function listAllPackages()
  1484.     {
  1485.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1486.             return $e;
  1487.         }
  1488.         $ret = $this->_listAllPackages();
  1489.         $this->_unlock();
  1490.         return $ret;
  1491.     }
  1492.  
  1493.     // }}}
  1494.     // {{{ listChannel()
  1495.  
  1496.     function listChannels()
  1497.     {
  1498.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1499.             return $e;
  1500.         }
  1501.         $ret = $this->_listChannels();
  1502.         $this->_unlock();
  1503.         return $ret;
  1504.     }
  1505.  
  1506.     // }}}
  1507.     // {{{ addPackage()
  1508.  
  1509.     /**
  1510.      * Add an installed package to the registry
  1511.      * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
  1512.      *               that will be passed to {@link addPackage2()}
  1513.      * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
  1514.      * @return bool success of saving
  1515.      */
  1516.     function addPackage($package, $info)
  1517.     {
  1518.         if (is_object($info)) {
  1519.             return $this->addPackage2($info);
  1520.         }
  1521.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1522.             return $e;
  1523.         }
  1524.         $ret = $this->_addPackage($package, $info);
  1525.         $this->_unlock();
  1526.         if ($ret) {
  1527.             if (!class_exists('PEAR_PackageFile_v1')) {
  1528.                 require_once 'PEAR/PackageFile/v1.php';
  1529.             }
  1530.             $pf = new PEAR_PackageFile_v1;
  1531.             $pf->setConfig($this->_config);
  1532.             $pf->fromArray($info);
  1533.             $this->_dependencyDB->uninstallPackage($pf);
  1534.             $this->_dependencyDB->installPackage($pf);
  1535.         }
  1536.         return $ret;
  1537.     }
  1538.  
  1539.     // }}}
  1540.     // {{{ addPackage2()
  1541.  
  1542.     function addPackage2($info)
  1543.     {
  1544.         if (!is_object($info)) {
  1545.             return $this->addPackage($info['package'], $info);
  1546.         }
  1547.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1548.             return $e;
  1549.         }
  1550.         $ret = $this->_addPackage2($info);
  1551.         $this->_unlock();
  1552.         if ($ret) {
  1553.             $this->_dependencyDB->uninstallPackage($info);
  1554.             $this->_dependencyDB->installPackage($info);
  1555.         }
  1556.         return $ret;
  1557.     }
  1558.  
  1559.     // }}}
  1560.     // {{{ updateChannel()
  1561.  
  1562.     /**
  1563.      * For future expandibility purposes, separate this
  1564.      * @param PEAR_ChannelFile
  1565.      */
  1566.     function updateChannel($channel, $lastmodified = null)
  1567.     {
  1568.         if ($channel->getName() == '__uri') {
  1569.             return false;
  1570.         }
  1571.         return $this->addChannel($channel, $lastmodified, true);
  1572.     }
  1573.  
  1574.     // }}}
  1575.     // {{{ deleteChannel()
  1576.  
  1577.     /**
  1578.      * Deletion fails if there are any packages installed from the channel
  1579.      * @param string|PEAR_ChannelFile channel name
  1580.      * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
  1581.      */
  1582.     function deleteChannel($channel)
  1583.     {
  1584.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1585.             return $e;
  1586.         }
  1587.         $ret = $this->_deleteChannel($channel);
  1588.         $this->_unlock();
  1589.         if ($ret && is_a($this->_config, 'PEAR_Config')) {
  1590.             $this->_config->setChannels($this->listChannels());
  1591.         }
  1592.         return $ret;
  1593.     }
  1594.  
  1595.     // }}}
  1596.     // {{{ addChannel()
  1597.  
  1598.     /**
  1599.      * @param PEAR_ChannelFile Channel object
  1600.      * @param string Last-Modified header from HTTP for caching
  1601.      * @return boolean|PEAR_Error True on creation, false if it already exists
  1602.      */
  1603.     function addChannel($channel, $lastmodified = false, $update = false)
  1604.     {
  1605.         if (!is_a($channel, 'PEAR_ChannelFile')) {
  1606.             return false;
  1607.         }
  1608.         if (!$channel->validate()) {
  1609.             return false;
  1610.         }
  1611.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1612.             return $e;
  1613.         }
  1614.         $ret = $this->_addChannel($channel, $update, $lastmodified);
  1615.         $this->_unlock();
  1616.         if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
  1617.             $this->_config->setChannels($this->listChannels());
  1618.         }
  1619.         return $ret;
  1620.     }
  1621.  
  1622.     // }}}
  1623.     // {{{ deletePackage()
  1624.  
  1625.     function deletePackage($package, $channel = 'pear.php.net')
  1626.     {
  1627.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1628.             return $e;
  1629.         }
  1630.         $file = $this->_packageFileName($package, $channel);
  1631.         $ret = @unlink($file);
  1632.         $this->_rebuildFileMap();
  1633.         $this->_unlock();
  1634.         $p = array('channel' => $channel, 'package' => $package);
  1635.         $this->_dependencyDB->uninstallPackage($p);
  1636.         return $ret;
  1637.     }
  1638.  
  1639.     // }}}
  1640.     // {{{ updatePackage()
  1641.  
  1642.     function updatePackage($package, $info, $merge = true)
  1643.     {
  1644.         if (is_object($info)) {
  1645.             return $this->updatePackage2($info, $merge);
  1646.         }
  1647.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1648.             return $e;
  1649.         }
  1650.         $ret = $this->_updatePackage($package, $info, $merge);
  1651.         $this->_unlock();
  1652.         if ($ret) {
  1653.             $pf = new PEAR_PackageFile_v1;
  1654.             $pf->setConfig($this->_config);
  1655.             $pf->fromArray($this->packageInfo($package));
  1656.             $this->_dependencyDB->uninstallPackage($pf);
  1657.             $this->_dependencyDB->installPackage($pf);
  1658.         }
  1659.         return $ret;
  1660.     }
  1661.  
  1662.     // }}}
  1663.     // {{{ updatePackage2()
  1664.  
  1665.     function updatePackage2($info)
  1666.     {
  1667.         if (!is_object($info)) {
  1668.             return $this->updatePackage($info['package'], $info, $merge);
  1669.         }
  1670.         if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
  1671.             return false;
  1672.         }
  1673.         if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  1674.             return $e;
  1675.         }
  1676.         $ret = $this->_updatePackage2($info);
  1677.         $this->_unlock();
  1678.         if ($ret) {
  1679.             $this->_dependencyDB->uninstallPackage($info);
  1680.             $this->_dependencyDB->installPackage($info);
  1681.         }
  1682.         return $ret;
  1683.     }
  1684.  
  1685.     // }}}
  1686.     // {{{ getChannel()
  1687.     /**
  1688.      * @param string channel name
  1689.      * @param bool whether to strictly return raw channels (no aliases)
  1690.      * @return PEAR_ChannelFile|false
  1691.      */
  1692.     function &getChannel($channel, $noaliases = false)
  1693.     {
  1694.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1695.             return $e;
  1696.         }
  1697.         $ret = &$this->_getChannel($channel, $noaliases);
  1698.         $this->_unlock();
  1699.         return $ret;
  1700.     }
  1701.  
  1702.     // }}}
  1703.     // {{{ getPackage()
  1704.     /**
  1705.      * @param string package name
  1706.      * @param string channel name
  1707.      * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
  1708.      */
  1709.     function &getPackage($package, $channel = 'pear.php.net')
  1710.     {
  1711.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1712.             return $e;
  1713.         }
  1714.         $pf = &$this->_getPackage($package, $channel);
  1715.         $this->_unlock();
  1716.         return $pf;
  1717.     }
  1718.  
  1719.     // }}}
  1720.  
  1721.     /**
  1722.      * Get PEAR_PackageFile_v[1/2] objects representing the contents of
  1723.      * a dependency group that are installed.
  1724.      *
  1725.      * This is used at uninstall-time
  1726.      * @param array
  1727.      * @return array|false
  1728.      */
  1729.     function getInstalledGroup($group)
  1730.     {
  1731.         $ret = array();
  1732.         if (isset($group['package'])) {
  1733.             if (!isset($group['package'][0])) {
  1734.                 $group['package'] = array($group['package']);
  1735.             }
  1736.             foreach ($group['package'] as $package) {
  1737.                 $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
  1738.                 $p = &$this->getPackage($package['name'], $depchannel);
  1739.                 if ($p) {
  1740.                     $save = &$p;
  1741.                     $ret[] = &$save;
  1742.                 }
  1743.             }
  1744.         }
  1745.         if (isset($group['subpackage'])) {
  1746.             if (!isset($group['subpackage'][0])) {
  1747.                 $group['subpackage'] = array($group['subpackage']);
  1748.             }
  1749.             foreach ($group['subpackage'] as $package) {
  1750.                 $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
  1751.                 $p = &$this->getPackage($package['name'], $depchannel);
  1752.                 if ($p) {
  1753.                     $save = &$p;
  1754.                     $ret[] = &$save;
  1755.                 }
  1756.             }
  1757.         }
  1758.         if (!count($ret)) {
  1759.             return false;
  1760.         }
  1761.         return $ret;
  1762.     }
  1763.  
  1764.     // {{{ getChannelValidator()
  1765.     /**
  1766.      * @param string channel name
  1767.      * @return PEAR_Validate|false
  1768.      */
  1769.     function &getChannelValidator($channel)
  1770.     {
  1771.         $chan = $this->getChannel($channel);
  1772.         if (!$chan) {
  1773.             return $chan;
  1774.         }
  1775.         $val = $chan->getValidationObject();
  1776.         return $val;
  1777.     }
  1778.     // }}}
  1779.     // {{{ getChannels()
  1780.     /**
  1781.      * @param string channel name
  1782.      * @return array an array of PEAR_ChannelFile objects representing every installed channel
  1783.      */
  1784.     function &getChannels()
  1785.     {
  1786.         $ret = array();
  1787.         if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1788.             return $e;
  1789.         }
  1790.         foreach ($this->_listChannels() as $channel) {
  1791.             $ret[] = &$this->_getChannel($channel);
  1792.         }
  1793.         $this->_unlock();
  1794.         return $ret;
  1795.     }
  1796.  
  1797.     // }}}
  1798.     // {{{ checkFileMap()
  1799.  
  1800.     /**
  1801.      * Test whether a file or set of files belongs to a package.
  1802.      *
  1803.      * If an array is passed in
  1804.      * @param string|array file path, absolute or relative to the pear
  1805.      *                     install dir
  1806.      * @param string|array name of PEAR package or array('package' => name, 'channel' =>
  1807.      *                     channel) of a package that will be ignored
  1808.      * @param string API version - 1.1 will exclude any files belonging to a package
  1809.      * @param array private recursion variable
  1810.      * @return array|false which package and channel the file belongs to, or an empty
  1811.      *                     string if the file does not belong to an installed package,
  1812.      *                     or belongs to the second parameter's package
  1813.      */
  1814.     function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
  1815.     {
  1816.         if (is_array($path)) {
  1817.             static $notempty;
  1818.             if (empty($notempty)) {
  1819.                 if (!class_exists('PEAR_Installer_Role')) {
  1820.                     require_once 'PEAR/Installer/Role.php';
  1821.                 }
  1822.                 $notempty = create_function('$a','return !empty($a);');
  1823.             }
  1824.             $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
  1825.                 : strtolower($package);
  1826.             $pkgs = array();
  1827.             foreach ($path as $name => $attrs) {
  1828.                 if (is_array($attrs)) {
  1829.                     if (isset($attrs['install-as'])) {
  1830.                         $name = $attrs['install-as'];
  1831.                     }
  1832.                     if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
  1833.                         // these are not installed
  1834.                         continue;
  1835.                     }
  1836.                     if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
  1837.                         $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
  1838.                     }
  1839.                     if (isset($attrs['baseinstalldir'])) {
  1840.                         $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
  1841.                     }
  1842.                 }
  1843.                 $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
  1844.                 if (PEAR::isError($pkgs[$name])) {
  1845.                     return $pkgs[$name];
  1846.                 }
  1847.             }
  1848.             return array_filter($pkgs, $notempty);
  1849.         }
  1850.         if (empty($this->filemap_cache)) {
  1851.             if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  1852.                 return $e;
  1853.             }
  1854.             $err = $this->_readFileMap();
  1855.             $this->_unlock();
  1856.             if (PEAR::isError($err)) {
  1857.                 return $err;
  1858.             }
  1859.         }
  1860.         if (!$attrs) {
  1861.             $attrs = array('role' => 'php'); // any old call would be for PHP role only
  1862.         }
  1863.         if (isset($this->filemap_cache[$attrs['role']][$path])) {
  1864.             if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
  1865.                 return false;
  1866.             }
  1867.             return $this->filemap_cache[$attrs['role']][$path];
  1868.         }
  1869.         $l = strlen($this->install_dir);
  1870.         if (substr($path, 0, $l) == $this->install_dir) {
  1871.             $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
  1872.         }
  1873.         if (isset($this->filemap_cache[$attrs['role']][$path])) {
  1874.             if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
  1875.                 return false;
  1876.             }
  1877.             return $this->filemap_cache[$attrs['role']][$path];
  1878.         }
  1879.         return false;
  1880.     }
  1881.  
  1882.     // }}}
  1883.     // {{{ apiVersion()
  1884.     /**
  1885.      * Get the expected API version.  Channels API is version 1.1, as it is backwards
  1886.      * compatible with 1.0
  1887.      * @return string
  1888.      */
  1889.     function apiVersion()
  1890.     {
  1891.         return '1.1';
  1892.     }
  1893.     // }}}
  1894.  
  1895.  
  1896.     /**
  1897.      * Parse a package name, or validate a parsed package name array
  1898.      * @param string|array pass in an array of format
  1899.      *                     array(
  1900.      *                      'package' => 'pname',
  1901.      *                     ['channel' => 'channame',]
  1902.      *                     ['version' => 'version',]
  1903.      *                     ['state' => 'state',]
  1904.      *                     ['group' => 'groupname'])
  1905.      *                     or a string of format
  1906.      *                     [channel://][channame/]pname[-version|-state][/group=groupname]
  1907.      * @return array|PEAR_Error
  1908.      */
  1909.     function parsePackageName($param, $defaultchannel = 'pear.php.net')
  1910.     {
  1911.         $saveparam = $param;
  1912.         if (is_array($param)) {
  1913.             // convert to string for error messages
  1914.             $saveparam = $this->parsedPackageNameToString($param);
  1915.             // process the array
  1916.             if (!isset($param['package'])) {
  1917.                 return PEAR::raiseError('parsePackageName(): array $param ' .
  1918.                     'must contain a valid package name in index "param"',
  1919.                     'package', null, null, $param);
  1920.             }
  1921.             if (!isset($param['uri'])) {
  1922.                 if (!isset($param['channel'])) {
  1923.                     $param['channel'] = $defaultchannel;
  1924.                 }
  1925.             } else {
  1926.                 $param['channel'] = '__uri';
  1927.             }
  1928.         } else {
  1929.             $components = @parse_url($param);
  1930.             if (isset($components['scheme'])) {
  1931.                 if ($components['scheme'] == 'http') {
  1932.                     // uri package
  1933.                     $param = array('uri' => $param, 'channel' => '__uri');
  1934.                 } elseif($components['scheme'] != 'channel') {
  1935.                     return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
  1936.                         'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
  1937.                 }
  1938.             }
  1939.             if (!isset($components['path'])) {
  1940.                 return PEAR::raiseError('parsePackageName(): array $param ' .
  1941.                     'must contain a valid package name in "' . $param . '"',
  1942.                     'package', null, null, $param);
  1943.             }
  1944.             if (isset($components['host'])) {
  1945.                 // remove the leading "/"
  1946.                 $components['path'] = substr($components['path'], 1);
  1947.             }
  1948.             if (!isset($components['scheme'])) {
  1949.                 if (strpos($components['path'], '/') !== false) {
  1950.                     if ($components['path']{0} == '/') {
  1951.                         return PEAR::raiseError('parsePackageName(): this is not ' .
  1952.                             'a package name, it begins with "/" in "' . $param . '"',
  1953.                             'invalid', null, null, $param);
  1954.                     }
  1955.                     $parts = explode('/', $components['path']);
  1956.                     $components['host'] = array_shift($parts);
  1957.                     if (count($parts) > 1) {
  1958.                         $components['path'] = array_pop($parts);
  1959.                         $components['host'] .= '/' . implode('/', $parts);
  1960.                     } else {
  1961.                         $components['path'] = implode('/', $parts);
  1962.                     }
  1963.                 } else {
  1964.                     $components['host'] = $defaultchannel;
  1965.                 }
  1966.             } else {
  1967.                 if (strpos($components['path'], '/')) {
  1968.                     $parts = explode('/', $components['path']);
  1969.                     $components['path'] = array_pop($parts);
  1970.                     $components['host'] .= '/' . implode('/', $parts);
  1971.                 }
  1972.             }
  1973.  
  1974.             if (is_array($param)) {
  1975.                 $param['package'] = $components['path'];
  1976.             } else {
  1977.                 $param = array(
  1978.                     'package' => $components['path']
  1979.                     );
  1980.                 if (isset($components['host'])) {
  1981.                     $param['channel'] = $components['host'];
  1982.                 }
  1983.             }
  1984.             if (isset($components['fragment'])) {
  1985.                 $param['group'] = $components['fragment'];
  1986.             }
  1987.             if (isset($components['user'])) {
  1988.                 $param['user'] = $components['user'];
  1989.             }
  1990.             if (isset($components['pass'])) {
  1991.                 $param['pass'] = $components['pass'];
  1992.             }
  1993.             if (isset($components['query'])) {
  1994.                 parse_str($components['query'], $param['opts']);
  1995.             }
  1996.             // check for extension
  1997.             $pathinfo = pathinfo($param['package']);
  1998.             if (isset($pathinfo['extension']) &&
  1999.                   in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
  2000.                 $param['extension'] = $pathinfo['extension'];
  2001.                 $param['package'] = substr($pathinfo['basename'], 0,
  2002.                     strlen($pathinfo['basename']) - 4);
  2003.             }
  2004.             // check for version
  2005.             if (strpos($param['package'], '-')) {
  2006.                 $test = explode('-', $param['package']);
  2007.                 if (count($test) != 2) {
  2008.                     return PEAR::raiseError('parsePackageName(): only one version/state ' .
  2009.                         'delimiter "-" is allowed in "' . $saveparam . '"',
  2010.                         'version', null, null, $param);
  2011.                 }
  2012.                 list($param['package'], $param['version']) = $test;
  2013.             }
  2014.         }
  2015.         // validation
  2016.         $info = $this->channelExists($param['channel']);
  2017.         if (PEAR::isError($info)) {
  2018.             return $info;
  2019.         }
  2020.         if (!$info) {
  2021.             return PEAR::raiseError('unknown channel "' . $param['channel'] .
  2022.                 '" in "' . $saveparam . '"', 'channel', null, null, $param);
  2023.         }
  2024.         $chan = $this->getChannel($param['channel']);
  2025.         if (PEAR::isError($chan)) {
  2026.             return $chan;
  2027.         }
  2028.         if (!$chan) {
  2029.             return PEAR::raiseError("Exception: corrupt registry, could not " .
  2030.                 "retrieve channel " . $param['channel'] . " information",
  2031.                 'registry', null, null, $param);
  2032.         }
  2033.         $param['channel'] = $chan->getName();
  2034.         $validate = $chan->getValidationObject();
  2035.         $vpackage = $chan->getValidationPackage();
  2036.         // validate package name
  2037.         if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
  2038.             return PEAR::raiseError('parsePackageName(): invalid package name "' .
  2039.                 $param['package'] . '" in "' . $saveparam . '"',
  2040.                 'package', null, null, $param);
  2041.         }
  2042.         if (isset($param['group'])) {
  2043.             if (!PEAR_Validate::validGroupName($param['group'])) {
  2044.                 return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
  2045.                     '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
  2046.                     $param);
  2047.             }
  2048.         }
  2049.         if (isset($param['state'])) {
  2050.             if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
  2051.                 return PEAR::raiseError('parsePackageName(): state "' . $param['state']
  2052.                     . '" is not a valid state in "' . $saveparam . '"',
  2053.                     'state', null, null, $param);
  2054.             }
  2055.         }
  2056.         if (isset($param['version'])) {
  2057.             if (isset($param['state'])) {
  2058.                 return PEAR::raiseError('parsePackageName(): cannot contain both ' .
  2059.                     'a version and a stability (state) in "' . $saveparam . '"',
  2060.                     'version/state', null, null, $param);
  2061.             }
  2062.             // check whether version is actually a state
  2063.             if (in_array(strtolower($param['version']), $validate->getValidStates())) {
  2064.                 $param['state'] = strtolower($param['version']);
  2065.                 unset($param['version']);
  2066.             } else {
  2067.                 if (!$validate->validVersion($param['version'])) {
  2068.                     return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
  2069.                         '" is neither a valid version nor a valid state in "' .
  2070.                         $saveparam . '"', 'version/state', null, null, $param);
  2071.                 }                    
  2072.             }
  2073.         }
  2074.         return $param;
  2075.     }
  2076.  
  2077.     /**
  2078.      * @param array
  2079.      * @return string
  2080.      */
  2081.     function parsedPackageNameToString($parsed, $brief = false)
  2082.     {
  2083.         if (is_string($parsed)) {
  2084.             return $parsed;
  2085.         }
  2086.         if (is_object($parsed)) {
  2087.             $p = $parsed;
  2088.             $parsed = array(
  2089.                 'package' => $p->getPackage(),
  2090.                 'channel' => $p->getChannel(),
  2091.                 'version' => $p->getVersion(),
  2092.             );
  2093.         }
  2094.         if (isset($parsed['uri'])) {
  2095.             return $parsed['uri'];
  2096.         }
  2097.         if ($brief) {
  2098.             if ($channel = $this->channelAlias($parsed['channel'])) {
  2099.                 return $channel . '/' . $parsed['package'];
  2100.             }
  2101.         }
  2102.         $upass = '';
  2103.         if (isset($parsed['user'])) {
  2104.             $upass = $parsed['user'];
  2105.             if (isset($parsed['pass'])) {
  2106.                 $upass .= ':' . $parsed['pass'];
  2107.             }
  2108.             $upass = "$upass@";
  2109.         }
  2110.         $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
  2111.         if (isset($parsed['version']) || isset($parsed['state'])) {
  2112.             $ret .= '-' . @$parsed['version'] . @$parsed['state'];
  2113.         }
  2114.         if (isset($parsed['extension'])) {
  2115.             $ret .= '.' . $parsed['extension'];
  2116.         }
  2117.         if (isset($parsed['opts'])) {
  2118.             $ret .= '?';
  2119.             foreach ($parsed['opts'] as $name => $value) {
  2120.                 $parsed['opts'][$name] = "$name=$value";
  2121.             }
  2122.             $ret .= implode('&', $parsed['opts']);
  2123.         }
  2124.         if (isset($parsed['group'])) {
  2125.             $ret .= '#' . $parsed['group'];
  2126.         }
  2127.         return $ret;
  2128.     }
  2129. }
  2130.  
  2131. ?>
  2132.